﻿#include "stdafx.h"
#include "sbcs.h"
#include "extend/sbmem.h"
#include "kstub_mutex.h"
#include "zlib/zlib.h"

// 메모리 테이블
static kMemTable* _k_mem_table = NULL;


//////////////////////////////////////////////////////////////////////////
// 메모리

/// 메모리 테이블 설정.
/// @date 2013-12-22
/// @param [in] tbl 테이블
/// @return 성공하면 참을 실패하면 거짓을 반환한다.
kcham k_memtable(kMemTable* tbl)
{
	k_return_value_if_fail(tbl != NULL, FALSE);
	k_return_value_if_fail(tbl->free != NULL, FALSE);
	k_return_value_if_fail(tbl->alloc != NULL, FALSE);
	k_return_value_if_fail(tbl->allocf != NULL, FALSE);

	_k_mem_table = tbl;

	return TRUE;
}

/// 메모리 블럭 크기 계산.
/// @date 2013-12-22
/// @param size 크기.
/// @return 계산된 블럭.
ksize_t k_memcalcblock(ksize_t size)
{
	return k_membsize(size);
}

/// 메모리 제거.
/// @date 2013-12-22
/// @param ptr 포인터.
void k_free(kpointer ptr)
{
#ifndef SB_NO_MEM
	k_init();

	if (ptr)
	{
		kMemBlock* node = k_memhdr(ptr);
		k_assert_0(node->tbl != NULL);
		node->tbl->free(ptr);
	}
#else
	if (ptr)
		free(ptr);
#endif
}

/// 메모리 할당. 메모리는 0으로 초기화 된다.
/// @date 2013-12-22
/// @param size 크기.
/// @return 할당 포인터.
kpointer k_alloc(ksize_t size)
{
#ifndef SB_NO_MEM
	kMemBlock* node;
	kpointer ptr;

	k_return_value_if_fail(size != 0, NULL);

	k_init();

	node = k_memhdr(_k_mem_table->alloc(k_memcalcblock(size)));
	node->filename = NULL;
	node->line = 0;
	node->size = size;

	ptr = k_memptr(node);

#if _SB_DEBUG_
	k_memtest(ptr);
#endif

	return ptr;
#else
	return size == 0 ? NULL : calloc(size, sizeof(kbyte));
#endif
}

/// 메모리 할당.
/// @date 2013-12-22
/// @param size 크기.
/// @return 할당된 포인터.
kpointer k_allocf(ksize_t size)
{
#ifndef SB_NO_MEM
	kMemBlock* node;
	kpointer ptr;

	k_return_value_if_fail(size != 0, NULL);

	k_init();

	node = k_memhdr(_k_mem_table->allocf(k_memcalcblock(size)));
	node->filename = NULL;
	node->line = 0;
	node->size = size;

	ptr = k_memptr(node);

#if _SB_DEBUG_
	k_memtest(ptr);
#endif

	return ptr;
#else
	return size == 0 ? NULL : malloc(size);
#endif
}

/// 메모리 재할당.
/// @date 2013-12-22
/// @param ptr  재할당할 포인터.
/// @param size 크기.
/// @return 재할당된 새로운 포인터.
kpointer k_realloc(kpointer ptr, ksize_t size)
{
#ifndef SB_NO_MEM
	kMemBlock* node;

	k_init();

	if (!ptr)
		return k_allocf(size);
	else
	{
		node = k_memhdr(ptr);
		k_assert_0(node->tbl != NULL);

		if (size == 0)
		{
			// 제거
			node->tbl->free(ptr);
			return NULL;
		}
		else
		{
			ksize_t block = k_memcalcblock(size);

			if (block <= node->block)
			{
				// 블럭이 같으면 그냥 씀
				node->size = size;
				return ptr;
			}
			else
			{
				// 새블럭 만들고 복사
				kMemBlock* newnode = k_memhdr(_k_mem_table->allocf(block));
				newnode->filename = NULL;
				newnode->line = 0;
				newnode->size = size;

				if (node->size)
					memcpy(k_memptr(newnode), k_memptr(node), node->size);

				node->tbl->free(ptr);
				return k_memptr(newnode);
			}
		}
	}
#else
	return realloc(ptr, size);
#endif
}

/// 메모리 할당, 디버그. 0으로 초기화 된다
/// @date 2013-12-22
/// @param size	    크기.
/// @param filename 파일 이름.
/// @param line	    줄번호.
/// @return 할당된 포인터.
kpointer k_alloc_dbg(ksize_t size, const char* filename, kint line)
{
#ifndef SB_NO_MEM
	kMemBlock* node;
	kpointer ptr;

	k_return_value_if_fail(size != 0, NULL);

	k_init();

	node = k_memhdr(_k_mem_table->alloc(k_memcalcblock(size)));
	node->filename = filename;
	node->line = (kushort)line;
	node->size = size;

	ptr = k_memptr(node);

#if _SB_DEBUG_
	k_memtest(ptr);
#endif

	return ptr;
#else
	K_NOUSE(filename);
	K_NOUSE(line);
	return size == 0 ? NULL : calloc(size, sizeof(kbyte));
#endif
}

/// 메모리 할당, 디버그.
/// @date 2013-12-22
/// @param size	    크기.
/// @param filename 파일 이름.
/// @param line	    줄번호.
/// @return 할당된 포인터.
kpointer k_allocf_dbg(ksize_t size, const char* filename, kint line)
{
#ifndef SB_NO_MEM
	kMemBlock* node;
	kpointer ptr;

	k_return_value_if_fail(size != 0, NULL);

	k_init();

	node = k_memhdr(_k_mem_table->allocf(k_memcalcblock(size)));
	node->filename = filename;
	node->line = (kushort)line;
	node->size = size;

	ptr = k_memptr(node);

#if _SB_DEBUG_
	k_memtest(ptr);
#endif

	return ptr;
#else
	K_NOUSE(filename);
	K_NOUSE(line);
	return size == 0 ? NULL : malloc(size);
#endif
}

/// 메모리 재할당, 디버그.
/// @date 2013-12-22
/// @param ptr	    포인터.
/// @param size	    크기.
/// @param filename 파일 이름.
/// @param line	    줄번호.
/// @return 재할당된 새로운 포인터.
kpointer k_realloc_dbg(kpointer ptr, ksize_t size, const char* filename, kint line)
{
#ifndef SB_NO_MEM
	kMemBlock* node;

	k_init();

	if (ptr == NULL)
		return k_allocf_dbg(size, filename, line);
	else
	{
		node = k_memhdr(ptr);
		k_assert_0(node->tbl != NULL);

		if (size == 0)
		{
			// 제거
			node->tbl->free(ptr);
			return NULL;
		}
		else
		{
			ksize_t block = k_memcalcblock(size);

			if (block <= node->block)
			{
				// 블럭이 같으면 그냥 씀
				node->filename = filename;
				node->line = (kushort)line;
				node->size = size;
				return ptr;
			}
			else
			{
				// 새블럭 만들고 복사
				kpointer newptr;
				kMemBlock* newnode = k_memhdr(_k_mem_table->allocf(block));
				newnode->filename = filename;
				newnode->line = (kushort)line;
				newnode->size = size;

				if (node->size)
					memcpy(k_memptr(newnode), k_memptr(node), node->size);

				node->tbl->free(ptr);

				newptr = k_memptr(newnode);

#if _SB_DEBUG_
				k_memtest(newptr);
#endif

				return newptr;
			}
		}
	}
#else
	K_NOUSE(filename);
	K_NOUSE(line);
	return realloc(ptr, size);
#endif
}

/// 마스크 카피.
/// @date 2013-12-22
/// @param dest 대상.
/// @param src  소스.
/// @param size 크기.
/// @param mask 마스크.
/// @return dest를 반환한다.
kpointer k_copymask(kpointer dest, kconstpointer src, ksize_t size, kint mask)
{
	kbyte* pd = (kbyte*)dest;
	kbyte* ps = (kbyte*)src;
	kbyte z;

	if (mask == 0)
	{
		for (; size; --size, ++pd, ++ps)
		{
			z = (kbyte)(255 - *ps);
			*pd = (kbyte)(z >> 4 | (z & 0x0F) << 4);
		}
	}
	else if (mask == 1)
	{
		for (; size; --size, ++pd, ++ps)
		{
			z = (kbyte)(255 - *ps);
			*pd = (kbyte)(z << 4 | (z & 0xF0) >> 4);
		}
	}

	return dest;
}

/// 메모리 압축. zlib를 이용한다
/// @date 2013-12-22
/// @param src				 소스.
/// @param srcsize			 소스 크기.
/// @param [in,out] destsize 이 값이 널이 아니면 다음 값이 들어간다: 할당된 메모리의 크기.
/// @return 할당된 포인터.
kpointer k_zcompress(kconstpointer src, ksize_t srcsize, ksize_t* destsize)
{
	kbyte* p;
	uLong tmp;

	k_return_value_if_fail(src != NULL, NULL);
	k_return_value_if_fail(srcsize != 0, NULL);

	tmp = ((uLong)srcsize + 12) / 1000;

	if (tmp < 1)
		tmp = (uLong)srcsize + 13;
	else
		tmp += (uLong)srcsize + 12;

	p = k_new(tmp, kbyte);

	if (compress((Bytef*)p, &tmp, (Bytef*)src, (uLong)srcsize) != Z_OK)
	{
		k_free(p);
		return NULL;
	}

	if (destsize)
		*destsize = tmp;

	return p;
}

/// 메모리 압축 해제.
/// @date 2013-12-22
/// @param src				 소스.
/// @param srcsize			 소스 크기.
/// @param bufsize			 버퍼 크기.
/// @param [in,out] destsize 이 값이 널이 아니면 다음 값이 들어간다: 할당된 메모리 크기.
/// @return 할당된 포인터.
kpointer k_zuncompress(kconstpointer src, ksize_t srcsize, ksize_t bufsize, ksize_t* destsize)
{
	kbyte* p;
	uLong size;

	k_return_value_if_fail(src != NULL, NULL);
	k_return_value_if_fail(srcsize != 0, NULL);

	size = (bufsize == 0) ? (uLong)srcsize * 5 + 12 : (uLong)bufsize;
	p = k_new(size, kbyte);

	if (uncompress((Bytef*)p, &size, (const Bytef*)src, (uLong)srcsize) != Z_OK)
	{
		k_free(p);
		return NULL;
	}

	if (destsize)
		*destsize = size;

	return p;
}

/// 메모리 압축 해제.
/// @date 2014-01-20
kcham k_zucmprsb(kpointer dst, ksize_t destsize, kconstpointer src, ksize_t srcsize)
{
	uLong size;

	k_return_value_if_fail(dst != NULL, FALSE);
	k_return_value_if_fail(destsize != 0, FALSE);
	k_return_value_if_fail(src != NULL, FALSE);
	k_return_value_if_fail(srcsize != 0, FALSE);

	size = (uLong)destsize;

	if (uncompress((Bytef*)dst, &size, (const Bytef*)src, (uLong)srcsize) != Z_OK)
		return FALSE;

	return TRUE;
}

/// 메모리 정렬 검사.
/// @date 2013-12-22
/// @param size 크기.
/// @return 정렬된 메모리 크기.
ksize_t k_memaligned(ksize_t size)
{
	ksize_t align = size % 16;
	ksize_t advise;

	if (align == 0)
		return 0;
	else
	{
		advise = (size + KMEM_PARA_SIZE - 1) & ~(KMEM_PARA_SIZE - 1);
		return advise;
	}
}

/// 메모리를 사람이 이해할 수 있는 단위로 계산하여 알려준다.
/// @date 2013-12-22
/// @param size		    크기.
/// @param [in,out] out 이 값이 널이 아니면 다음 값이 들어간다: 메모리 접두사(Giga, Mega, Kilo).
/// @return 접두사에 맞는 메모리 크기.
int k_memudch(ksize_t size, double* out)
{
	if (!out)
		return ' ';

	if (size > 1024 * 1024 * 1024)
	{
		*out = (double)size / (double)(1024 * 1024 * 1024);
		return 'g';
	}

	if (size > 1024 * 1024)
	{
		*out = (double)size / (double)(1024 * 1024);
		return 'm';
	}

	if (size > 1024)
	{
		*out = (double)size / (double)(1024);
		return 'k';
	}

	*out = (double)size;
	return ' ';
}

/// 메모리를 사람이 알 수 있게 문자열로 만들어 준다.
/// @date 2013-12-22
/// @param [in,out] buf 이 값이 널이 아니면 다음 값이 들어간다: 버퍼.
/// @param buflen	    버퍼 크기.
/// @param size		    메모리 크기.
/// @return 반환 버퍼의 길이.
kint k_memuds(char* buf, kint buflen, ksize_t size)
{
	double out;
	kint usage = k_memudch(size, &out);
	return usage == ' ' ?
		k_snprintf(buf, buflen, "%Lu ", size) :
		k_snprintf(buf, buflen, "%.2f %c", out, usage);
}

/// 메모리 테스트.
/// @date 2013-12-22
/// @param ptr 포인터.
/// @return 성공하면 참을 실패하면 거짓을 반환한다.
kcham k_memtest(kpointer ptr)
{
#ifndef SB_NO_MEM
	kMemBlock* node = k_memhdr(ptr);

	if (node &&
		k_heapisvalid(node) &&
		node->index != K_MAX_BYTE)
		return TRUE;
	else
		return FALSE;

#else
	return ptr != NULL;
#endif
}

/// 메모리 덤프.
/// @date 2013-12-22
/// @param ptr		    포인터.
/// @param size		    크기.
/// @param [in,out] buf 이 값이 널이 아니면 다음 값이 들어간다: 버퍼.
/// @param len		    길이.
/// @return buf를 반환한다.
char* k_memdmp(kconstpointer ptr, ksize_t size, char* buf, ksize_t len)
{
	const kbyte* mem = (const kbyte*)ptr;
	char* tmp = buf;
	ksize_t i, cnt;

	k_return_value_if_fail(ptr != NULL, NULL);
	k_return_value_if_fail(buf != NULL, NULL);

	if (size == 0 || len == 0)
	{
		*buf = '\0';
		return buf;
	}

	for (i = 0, cnt = 1; i < size && cnt < len; i++, cnt++)
	{
		kbyte ch = *mem;
		*tmp = (ch < 0x20 || ch > 0x7F) ? ' ' : (char)ch;
		mem++;
		tmp++;
	}

	*tmp = '\0';

	return buf;
}

/// 메모리 덤프.
/// @date 2013-12-22
/// @param ptr		    포인터.
/// @param size		    크기.
/// @param [in,out] buf 이 값이 널이 아니면 다음 값이 들어간다: 버퍼.
/// @param len		    길이.
/// @return buf를 반환한다.
char* k_memdmp_hex(kconstpointer ptr, ksize_t size, char* buf, ksize_t len)
{
	const kbyte* mem = (const kbyte*)ptr;
	char* tmp = buf;
	ksize_t i, cnt;

	k_return_value_if_fail(ptr != NULL, NULL);
	k_return_value_if_fail(buf != NULL, NULL);

	if (size == 0 || len == 0)
	{
		*buf = '\0';
		return buf;
	}

	for (i = 0, cnt = 4; i < size && cnt < len; i++, cnt += 3)
	{
		k_snprintf(tmp, 4, "%02X ", (int)*mem);
		mem++;
		tmp += 3;
	}

	*tmp = '\0';

	return buf;
}


//////////////////////////////////////////////////////////////////////////
// zlib 호환

/// Kz inflate reset.
/// @date 2013-12-22
/// @param strm strm.
/// @return An int.
KAPI int kz_inflate_reset(z_streamp strm)
{
	return inflateReset(strm);
}

/// Kz inflate.
/// @date 2013-12-22
/// @param strm  strm.
/// @param flush flush.
/// @return An int.
KAPI int kz_inflate(z_streamp strm, int flush)
{
	return inflate(strm, flush);
}

/// Kz inflate end.
/// @date 2013-12-22
/// @param strm strm.
/// @return An int.
KAPI int kz_inflate_end(z_streamp strm)
{
	return inflateEnd(strm);
}

/// Kz inflate 초기화.
/// @date 2013-12-22
/// @param strm strm.
/// @return An int.
KAPI int kz_inflate_init(z_streamp strm)
{
	return inflateInit(strm);
}

/// Kz CRC 32.
/// @date 2013-12-22
/// @param crc CRC.
/// @param buf 버퍼.
/// @param len 길이.
/// @return An uLong.
KAPI uLong kz_crc32(uLong crc, const Bytef *buf, uInt len)
{
	return crc32(crc, buf, len);
}

/// Kz deflate.
/// @date 2013-12-22
/// @param strm  strm.
/// @param flush flush.
/// @return An int.
KAPI int kz_deflate(z_streamp strm, int flush)
{
	return deflate(strm, flush);
}

/// Kz deflate end.
/// @date 2013-12-22
/// @param strm strm.
/// @return An int.
KAPI int kz_deflateEnd(z_streamp strm)
{
	return deflateEnd(strm);
}

/// Kz deflate reset.
/// @date 2013-12-22
/// @param strm strm.
/// @return An int.
KAPI int kz_deflateReset(z_streamp strm)
{
	return deflateReset(strm);
}

/// Kz deflate 초기화 2.
/// @date 2013-12-22
/// @param strm		  strm.
/// @param level	  레벨.
/// @param method	  method.
/// @param windowBits window bits.
/// @param memLevel   메모리 레벨.
/// @param strategy   strategy.
/// @return An int.
KAPI int kz_deflateInit2(z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy)
{
	return deflateInit2(strm, level, method, windowBits, memLevel, strategy);
}
